package gatperdut.chato.core.echoes;

import gatperdut.chato.constants.CSConstantsFood;
import gatperdut.chato.constants.CSConstantsGender;
import gatperdut.chato.constants.CSConstantsInventory;
import gatperdut.chato.constants.CSConstantsMovement;
import gatperdut.chato.constants.CSConstantsMovement.Direction;
import gatperdut.chato.constants.CSConstantsString;
import gatperdut.chato.constants.CSConstantsString.Possessive;
import gatperdut.chato.core.characters.BaseController;
import gatperdut.chato.core.craft.CraftController;
import gatperdut.chato.core.craft.CraftStepController;
import gatperdut.chato.core.craft.CraftToolController;
import gatperdut.chato.core.item.DrinkController;
import gatperdut.chato.core.item.FoodController;
import gatperdut.chato.core.item.ItemController;
import gatperdut.chato.core.room.DoorController;
import gatperdut.chato.core.room.RoomController;
import gatperdut.chato.model.Chato.Constant.WearLoc;

import java.util.Collection;
import java.util.Map.Entry;

public class EchoesController {

    private BaseController baseController;

    private String preparePossessivesAndObjectsEmote( BaseController bc, String emote ) {
        String emotePossessiveNouns = emote.replace( bc.getCharacter().getShortDesc() + "'ss", "yours" );
        String emotePossessives = emotePossessiveNouns.replace( bc.getCharacter().getShortDesc() + "'s", "your" );
        return emotePossessives.replace( bc.getCharacter().getShortDesc(), "you" );
    }

    private String prepareGeneralEmote( BaseController bc, String emote ) {
        if ( "".equals( emote ) ) {
            return ".";
        }
        String atPossessiveReplacedEmote = emote.replace( "@'s", CSConstantsGender.getGenderPossessive( bc.getCharacter().getGender() ) );
        return atPossessiveReplacedEmote.replace( "@", getBaseController().getCharacter().getShortDesc() ) + ".";
    }

    private String prepareLastPartEmote( BaseController bc, String emote ) {
        if ( "".equals( emote ) ) {
            return ".";
        }

        return ", " + preparePossessivesAndObjectsEmote( bc, emote ) + ".";
    }

    private String prepareCommunicationEmote( BaseController bc, String emote ) {
        if ( "".equals( emote ) ) {
            return ":" + CSConstantsString.NEWLINE + "  \"";
        }

        String possessiveAndObjectsEmote = preparePossessivesAndObjectsEmote( bc, emote );

        if ( !getBaseController().getCharacter().getKnownLanguages().contains( bc.getCharacter().getCurrentLanguage() ) ) {
            return ", " + possessiveAndObjectsEmote;
        }

        return ", " + possessiveAndObjectsEmote + ":" + CSConstantsString.NEWLINE + "  \"";
    }

    public void txSay( String emote, String speech ) {
        String replacedEmote = prepareCommunicationEmote( getBaseController(), emote );

        getBaseController().tx( "You say in " + getBaseController().getCommunicationLanguage() + replacedEmote + speech + "\"" );

        for ( BaseController bc : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( bc != getBaseController() ) {
                bc.getEchoes().rxSay( getBaseController(), emote, speech );
            }
        }
    }

    private void rxSay( BaseController bc, String emote, String speech ) {
        String replacedEmote = prepareCommunicationEmote( bc, emote );

        if ( !getBaseController().getCharacter().getKnownLanguages().contains( bc.getCharacter().getCurrentLanguage() ) ) {
            getBaseController().tx( bc.getCharacter().getShortDesc() + " says something" + replacedEmote + ", but you can't quite make out the words." );
            return;
        }

        getBaseController().tx( bc.getCharacter().getShortDesc() + " says in " + bc.getCommunicationLanguage() + replacedEmote + speech + "\"" );

    }

    public void txTell( BaseController bc, String emote, String speech ) {
        String replacedEmote = prepareCommunicationEmote( bc, emote );

        getBaseController().tx( "You tell " + bc.getCharacter().getShortDesc() + " in " + getBaseController().getCommunicationLanguage() + replacedEmote + speech + "\"" );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                if ( bc == other ) {
                    bc.getEchoes().rxTell( getBaseController(), emote, speech );
                }
                else {
                    other.getEchoes().rxTellForAnother( getBaseController(), emote, speech );
                }
            }
        }
    }

    private void rxTell( BaseController bc, String emote, String speech ) {
        String replacedEmote = prepareCommunicationEmote( bc, emote );

        if ( !getBaseController().getCharacter().getKnownLanguages().contains( bc.getCharacter().getCurrentLanguage() ) ) {
            getBaseController().tx( bc.getCharacter().getShortDesc() + " tells you something" + replacedEmote + ", but you can't quite make out the words." );
            return;
        }

        getBaseController().tx( bc.getCharacter().getShortDesc() + " tells you in " + bc.getCommunicationLanguage() + replacedEmote + speech + "\"" );
    }

    private void rxTellForAnother( BaseController bc, String emote, String speech ) {
        String replacedEmote = prepareCommunicationEmote( bc, emote );

        if ( !getBaseController().getCharacter().getKnownLanguages().contains( bc.getCharacter().getCurrentLanguage() ) ) {
            getBaseController().tx(
                    bc.getCharacter().getShortDesc() + " tells " + bc.getCharacter().getShortDesc() + " something" + replacedEmote
                            + ", but you can't quite make out the words." );
            return;
        }

        getBaseController().tx(
                bc.getCharacter().getShortDesc() + " tells " + bc.getCharacter().getShortDesc() + " in " + bc.getCommunicationLanguage() + replacedEmote + speech + "\"" );
    }

    public void txShout( String emote, String speech ) {
        String replacedEmote = prepareCommunicationEmote( getBaseController(), emote );

        getBaseController().tx( "You shout in " + getBaseController().getCommunicationLanguage() + replacedEmote + speech + "\"" );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxShoutSameRoom( getBaseController(), emote, speech );
            }
        }

        for ( Entry<Direction, RoomController> entry : getBaseController().getRoomController().getExitsController().getAdjacentRoomControllers().entrySet() ) {
            entry.getValue().rxShout( getBaseController(), CSConstantsMovement.getOpposite( entry.getKey() ), speech );
        }
    }

    private void rxShoutSameRoom( BaseController bc, String emote, String speech ) {
        String replacedEmote = prepareCommunicationEmote( bc, emote );

        if ( !getBaseController().getCharacter().getKnownLanguages().contains( bc.getCharacter().getCurrentLanguage() ) ) {
            getBaseController().tx( bc.getCharacter().getShortDesc() + " shouts something" + replacedEmote + ", but you can't quite make out the words." );
            return;
        }

        getBaseController().tx( bc.getCharacter().getShortDesc() + " shouts in " + bc.getCommunicationLanguage() + replacedEmote + speech + "\"" );
    }

    public void rxShoutAdjacentRoom( BaseController bc, Direction opposite, String speech ) {
        if ( !getBaseController().getCharacter().getKnownLanguages().contains( bc.getCharacter().getCurrentLanguage() ) ) {
            getBaseController().tx(
                    CSConstantsGender.getGenderShout( bc.getCharacter().getGender() ) + " shouts something " + opposite.getAsOrigin()
                            + ", but you can't quite make out the words." );
            return;
        }

        getBaseController().tx(
                CSConstantsGender.getGenderShout( bc.getCharacter().getGender() ) + " shouts " + opposite.getAsOrigin() + ":" + CSConstantsString.NEWLINE + "  \""
                        + speech + "\"" );
    }

    public void txDrop( ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You drop " + ic.getShortDesc() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxDrop( getBaseController(), ic, emote );
            }
        }
    }

    private void rxDrop( BaseController bc, ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " drops " + ic.getShortDesc() + replacedEmote );
    }

    public void txGetFromContainer( ItemController pick, ItemController container, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You take " + pick.getShortDesc() + " from " + container.getShortDesc() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxGetFromContainer( getBaseController(), pick, container, emote );
            }
        }
    }

    private void rxGetFromContainer( BaseController bc, ItemController pick, ItemController container, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " takes " + pick.getShortDesc() + " from " + container.getShortDesc() + replacedEmote );
    }

    public void txGetFromRoom( ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You take " + ic.getShortDesc() + replacedEmote );

        // TODO this kind of iterations should maybe get moved to the room
        // controller?
        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxGetFromRoom( getBaseController(), ic, emote );
            }
        }
    }

    private void rxGetFromRoom( BaseController bc, ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " takes " + ic.getShortDesc() + replacedEmote );
    }

    public void txGive( ItemController ic, BaseController bc, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You give " + ic.getShortDesc() + " to " + bc.getCharacter().getShortDesc() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                if ( bc == other ) {
                    bc.getEchoes().rxGive( getBaseController(), ic, emote );
                }
                else {
                    other.getEchoes().rxGiveForAnother( getBaseController(), ic, bc, emote );
                }
            }
        }
    }

    private void rxGive( BaseController bc, ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " gives you " + ic.getShortDesc() + replacedEmote );
    }

    private void rxGiveForAnother( BaseController giver, ItemController ic, BaseController whom, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( giver.getCharacter().getShortDesc() + " gives " + ic.getShortDesc() + " to " + whom.getCharacter().getShortDesc() + replacedEmote );
    }

    public void txPut( ItemController pick, ItemController container, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You put " + pick.getShortDesc() + " into " + container.getShortDesc() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxPut( getBaseController(), pick, container, emote );
            }
        }
    }

    public void txEmote( String emote ) {
        boolean containsAt = emote.contains( "@" );

        String replacedEmote = prepareGeneralEmote( getBaseController(), emote );

        if ( containsAt ) {
            getBaseController().tx( replacedEmote );
            for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
                if ( other != getBaseController() ) {
                    other.tx( preparePossessivesAndObjectsEmote( other, replacedEmote ) );
                }
            }
        }
        else {
            getBaseController().tx( getBaseController().getCharacter().getShortDesc() + " " + replacedEmote );
            for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
                if ( other != getBaseController() ) {
                    other.tx( getBaseController().getCharacter().getShortDesc() + " " + preparePossessivesAndObjectsEmote( other, replacedEmote ) );
                }
            }
        }
    }

    private void rxPut( BaseController bc, ItemController pick, ItemController container, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " puts " + pick.getShortDesc() + " in " + container.getShortDesc() + replacedEmote );
    }

    public void txMovement( Direction direction, RoomController previousRoom, RoomController nextRoom, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You start walking " + direction.getAsMovement() + replacedEmote );

        for ( BaseController other : previousRoom.getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxMovementLeaving( getBaseController(), direction, emote );
            }
        }

        for ( BaseController other : nextRoom.getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxMovementArriving( getBaseController(), CSConstantsMovement.getOpposite( direction ), emote );
            }
        }
    }

    private void rxMovementLeaving( BaseController bc, Direction direction, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " starts walking " + direction.getAsMovement() + replacedEmote );
    }

    private void rxMovementArriving( BaseController bc, Direction direction, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " is coming " + direction.getAsOrigin() + replacedEmote );
    }

    public void txCloseDoor( DoorController dc, Direction d, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You close " + dc.getDoor().getDesc() + " " + d.getAsDoor() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxCloseDoor( getBaseController(), dc, d, emote );
            }
        }
    }

    private void rxCloseDoor( BaseController bc, DoorController dc, Direction d, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " closes " + dc.getDoor().getDesc() + " " + d.getAsDoor() + replacedEmote );
    }

    public void txOpenDoor( DoorController dc, Direction d, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You open " + dc.getDoor().getDesc() + " " + d.getAsDoor() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxOpenDoor( getBaseController(), dc, d, emote );
            }
        }
    }

    private void rxOpenDoor( BaseController bc, DoorController dc, Direction d, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " opens " + dc.getDoor().getDesc() + " " + d.getAsDoor() + replacedEmote );
    }

    public void txLockDoor( DoorController dc, Direction d, ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You lock " + dc.getDoor().getDesc() + " " + d.getAsDoor() + " with " + ic.getShortDesc() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxLockDoor( getBaseController(), dc, d, ic, emote );
            }
        }
    }

    private void rxLockDoor( BaseController bc, DoorController dc, Direction d, ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx(
                bc.getCharacter().getShortDesc() + " locks " + dc.getDoor().getDesc() + " " + d.getAsDoor() + " with " + ic.getShortDesc() + replacedEmote );
    }

    public void txUnlockDoor( DoorController dc, Direction d, ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You unlock " + dc.getDoor().getDesc() + " " + d.getAsDoor() + " with " + ic.getShortDesc() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxUnlockDoor( getBaseController(), dc, d, ic, emote );
            }
        }
    }

    private void rxUnlockDoor( BaseController bc, DoorController dc, Direction d, ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx(
                bc.getCharacter().getShortDesc() + " unlocks " + dc.getDoor().getDesc() + " " + d.getAsDoor() + " with " + ic.getShortDesc() + replacedEmote );
    }

    public void txOpenContainer( ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You open " + ic.getShortDesc() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxOpenContainer( getBaseController(), ic, emote );
            }
        }
    }

    private void rxOpenContainer( BaseController bc, ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " opens " + ic.getShortDesc() + replacedEmote );
    }

    public void txCloseContainer( ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You close " + ic.getShortDesc() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxCloseContainer( getBaseController(), ic, emote );
            }
        }
    }

    private void rxCloseContainer( BaseController bc, ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " closes " + ic.getShortDesc() + replacedEmote );
    }

    public void txLockContainer( ItemController container, ItemController key, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You lock " + container.getShortDesc() + " with " + key.getShortDesc() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxLockContainer( getBaseController(), container, key, emote );
            }
        }
    }

    private void rxLockContainer( BaseController bc, ItemController container, ItemController key, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " locks " + container.getShortDesc() + " with " + key.getShortDesc() + replacedEmote );
    }

    public void txUnlockContainer( ItemController container, ItemController key, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You unlock " + container.getShortDesc() + " with " + key.getShortDesc() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxUnlockContainer( getBaseController(), container, key, emote );
            }
        }
    }

    private void rxUnlockContainer( BaseController bc, ItemController container, ItemController key, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " unlocks " + container.getShortDesc() + " with " + key.getShortDesc() + replacedEmote );
    }

    public void txThrow( ItemController ic, Direction d, RoomController rc, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You throw " + ic.getShortDesc() + " " + d.getAsMovement() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxThrowOriginRoom( getBaseController(), ic, d, emote );
            }
        }

        rc.rxThrow( ic, CSConstantsMovement.getOpposite( d ) );
    }

    private void rxThrowOriginRoom( BaseController bc, ItemController ic, Direction d, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " throws " + ic.getShortDesc() + " " + d.getAsMovement() + replacedEmote );
    }

    public void rxThrowAdjacentRoom( ItemController ic, Direction opposite ) {
        getBaseController().tx( ic.getShortDesc() + " is thrown in " + opposite.getAsOrigin() + "." );
    }

    public void txEat( ItemController ic, int amountToConsume, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        FoodController fc = (FoodController) ic.getAmountController();

        String amountString = CSConstantsFood.getEatingPortionDescription( amountToConsume, fc.getPortion() );

        getBaseController().tx( "You eat " + amountString + " " + ic.getShortDesc() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxEat( getBaseController(), ic, amountString, emote );
            }
        }
    }

    private void rxEat( BaseController bc, ItemController ic, String amountString, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " eats " + amountString + " " + ic.getShortDesc() + replacedEmote );
    }

    public void txDrink( ItemController ic, int amountToConsume, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        DrinkController dc = (DrinkController) ic.getAmountController();

        String amountString = CSConstantsFood.getDrinkingPortionDescription( amountToConsume, dc.getPortion() );

        getBaseController().tx(
                "You drink " + amountString + " of the " + CSConstantsFood.getFluidName( dc.getFluidContent() ) + " from " + ic.getShortDesc() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxDrink( getBaseController(), ic, amountString, emote );
            }
        }
    }

    private void rxDrink( BaseController bc, ItemController ic, String amountString, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        DrinkController dc = (DrinkController) ic.getAmountController();

        getBaseController().tx(
                bc.getCharacter().getShortDesc() + " drinks " + amountString + " of the " + CSConstantsFood.getFluidName( dc.getFluidContent() ) + " from "
                        + ic.getShortDesc() + replacedEmote );
    }

    public void txSpillOnGround( ItemController ic, boolean spilledAll, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        String all;
        if ( spilledAll ) {
            all = "all the ";
        }
        else {
            all = "";
        }

        getBaseController().tx(
                "You spill " + all + CSConstantsFood.getFluidName( ( (DrinkController) ic.getAmountController() ).getFluidContent() ) + " from " + ic.getShortDesc()
                        + " on the ground" + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxSpillLiquidOnGround( getBaseController(), ic, spilledAll, emote );
            }
        }
    }

    private void rxSpillLiquidOnGround( BaseController bc, ItemController ic, boolean spilledAll, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        String all;
        if ( spilledAll ) {
            all = "all the ";
        }
        else {
            all = "";
        }

        getBaseController().tx(
                bc.getCharacter().getShortDesc() + " spills " + all + CSConstantsFood.getFluidName( ( (DrinkController) ic.getAmountController() ).getFluidContent() )
                        + " from " + ic.getShortDesc() + " on the ground" + replacedEmote );
    }

    public void txSpillInContainer( ItemController source, ItemController newContainer, boolean spilledAll, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        String all;
        if ( spilledAll ) {
            all = "all the ";
        }
        else {
            all = "";
        }

        getBaseController().tx(
                "You spill " + all + CSConstantsFood.getFluidName( ( (DrinkController) source.getAmountController() ).getFluidContent() ) + " from "
                        + source.getShortDesc() + " into " + newContainer.getShortDesc() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxSpillInContainer( getBaseController(), source, newContainer, spilledAll, emote );
            }
        }
    }

    private void rxSpillInContainer( BaseController bc, ItemController source, ItemController newContainer, boolean spilledAll, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        String all;
        if ( spilledAll ) {
            all = "all the ";
        }
        else {
            all = "";
        }

        getBaseController().tx(
                bc.getCharacter().getShortDesc() + " spills " + all + CSConstantsFood.getFluidName( ( (DrinkController) source.getAmountController() ).getFluidContent() )
                        + " from " + source.getShortDesc() + " into " + newContainer.getShortDesc() + replacedEmote );
    }

    public void txFill( ItemController toBeFilled, ItemController toFillFrom, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx(
                "You fill " + toBeFilled.getShortDesc() + " with the "
                        + CSConstantsFood.getFluidName( ( (DrinkController) toFillFrom.getAmountController() ).getFluidContent() ) + " from " + toFillFrom.getShortDesc()
                        + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxFill( getBaseController(), toBeFilled, toFillFrom, emote );
            }
        }
    }

    private void rxFill( BaseController bc, ItemController toBeFilled, ItemController toFillFrom, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx(
                bc.getCharacter().getShortDesc() + " fills " + toBeFilled.getShortDesc() + " with the "
                        + CSConstantsFood.getFluidName( ( (DrinkController) toFillFrom.getAmountController() ).getFluidContent() ) + " from " + toFillFrom.getShortDesc()
                        + replacedEmote );
    }

    public void txEmptyOnGround( ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You empty the contents of " + ic.getShortDesc() + " on the ground" + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxEmptyOnGround( getBaseController(), ic, emote );
            }
        }
    }

    private void rxEmptyOnGround( BaseController bc, ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " empties the contents of " + ic.getShortDesc() + " on the ground" + replacedEmote );
    }

    public void txSwitchBothHands( ItemController r, ItemController l, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You switch " + r.getShortDesc() + " to your right hand and " + l.getShortDesc() + " to your left hand" + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxSwitchBothHands( getBaseController(), r, l, emote );
            }
        }
    }

    private void rxSwitchBothHands( BaseController bc, ItemController r, ItemController l, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );
        getBaseController().tx(
                bc.getCharacter().getShortDesc() + " switches " + r.getShortDesc() + " to the right hand and " + l.getShortDesc() + " to the left hand" + replacedEmote );
    }

    public void txSwitchOneHand( ItemController ic, WearLoc wearLoc, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You switch " + ic.getShortDesc() + " to your " + CSConstantsInventory.getWearLocName( wearLoc ) + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxSwitchOneHand( getBaseController(), ic, wearLoc, emote );
            }
        }
    }

    private void rxSwitchOneHand( BaseController bc, ItemController ic, WearLoc wearLoc, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx(
                bc.getCharacter().getShortDesc() + " switches " + ic.getShortDesc() + " to the " + CSConstantsInventory.getWearLocName( wearLoc ) + replacedEmote );
    }

    public void txWear( ItemController ic, WearLoc wearLoc, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx(
                "You wear " + ic.getShortDesc() + " " + CSConstantsInventory.getWearLocPossessiveInvName( wearLoc ).replace( "@", Possessive.YOUR.getValue() )
                        + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxWear( getBaseController(), ic, wearLoc, emote );
            }
        }
    }

    private void rxWear( BaseController bc, ItemController ic, WearLoc wearLoc, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        String possessive = CSConstantsString.getPossessiveForGender( bc.getCharacter().getGender() ).getValue();

        String wearLocPossName = CSConstantsInventory.getWearLocPossessiveInvName( wearLoc ).replace( "@", possessive );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " wears " + ic.getShortDesc() + " " + wearLocPossName + replacedEmote );
    }

    public void txRemove( ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( "You remove " + ic.getShortDesc() + replacedEmote );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxRemove( getBaseController(), ic, emote );
            }
        }
    }

    private void rxRemove( BaseController bc, ItemController ic, String emote ) {
        String replacedEmote = prepareLastPartEmote( getBaseController(), emote );

        getBaseController().tx( bc.getCharacter().getShortDesc() + " removes " + ic.getShortDesc() + replacedEmote );
    }

    public void txCraftInitialMessage( CraftController cc ) {
        processCraftMessages( cc.getCraft().getInitMessageSelf(), cc.getCraft().getInitMessageOthers(), cc.getCraftToolToController().values() );
    }

    public void txCraftSuccessMessage( CraftController cc ) {
        processCraftMessages( cc.getCraft().getSuccessMessageSelf(), cc.getCraft().getSuccessMessageOthers(), cc.getCraftToolToController().values() );
    }

    public void txCraftStepInitialMessage( CraftStepController csc ) {
        processCraftMessages( csc.getCraftStep().getInitMessageSelf(), csc.getCraftStep().getInitMessageOthers(), csc.getCraftToolToController().values() );
    }

    public void txCraftStepFailureMessage( CraftStepController csc ) {
        processCraftMessages( csc.getCraftStep().getFailMessageSelf(), csc.getCraftStep().getFailMessageOthers(), csc.getCraftToolToController().values() );
    }

    private void processCraftMessages( String self, String others, Collection<CraftToolController> ctcs ) {
        others = others.replace( "@'s", CSConstantsGender.getGenderPossessive( getBaseController().getCharacter().getGender() ) );
        others = others.replace( "@.", CSConstantsGender.getGenderPronoun( getBaseController().getCharacter().getGender() ) );
        others = others.replace( "@", getBaseController().getCharacter().getShortDesc() );

        for ( CraftToolController ctc : ctcs ) {
            self = self.replaceFirst( "#", ctc.getChosenTool().getShortDesc() );
            others = others.replaceFirst( "#", ctc.getChosenTool().getShortDesc() );
        }

        getBaseController().tx( self );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.tx( others );
            }
        }
    }

    public void txAttackMiss( BaseController tc ) {
        getBaseController().tx( "You swing at " + tc.getCharacter().getShortDesc() + " and miss." );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                if ( tc == other ) {
                    tc.getEchoes().rxAttackMiss( getBaseController() );
                }
                else {
                    other.getEchoes().rxAttackMissForAnother( getBaseController(), tc );
                }
            }
        }
    }

    private void rxAttackMiss( BaseController attacker ) {
        getBaseController().tx( attacker.getCharacter().getShortDesc() + " swings at you and misses." );
    }

    private void rxAttackMissForAnother( BaseController attacker, BaseController attacked ) {
        getBaseController().tx( attacker.getCharacter().getShortDesc() + " swings at " + attacked.getCharacter().getShortDesc() + " and misses." );
    }

    public void txAttackHit( BaseController tc, int dam ) {
        getBaseController().tx( "You hit " + tc.getCharacter().getShortDesc() + " for " + dam + " points." );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                if ( tc == other ) {
                    tc.getEchoes().rxAttackHit( getBaseController(), dam );
                }
                else {
                    other.getEchoes().rxAttackHitForAnother( getBaseController(), tc, dam );
                }
            }
        }
    }

    private void rxAttackHit( BaseController attacker, int dam ) {
        getBaseController().tx( attacker.getCharacter().getShortDesc() + " hits you for " + dam + " points." );
    }

    private void rxAttackHitForAnother( BaseController attacker, BaseController attacked, int dam ) {
        getBaseController().tx( attacker.getCharacter().getShortDesc() + " hits " + attacked.getCharacter().getShortDesc() + " for " + dam + " points." );
    }

    public void txShiftAttention( BaseController newTarget ) {
        getBaseController().tx( "You shift your attention to " + newTarget.getCharacter().getShortDesc() + "." );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                if ( newTarget == other ) {
                    newTarget.getEchoes().rxShiftAttention( getBaseController() );
                }
                else {
                    other.getEchoes().rxShiftAttentionForAnother( getBaseController(), newTarget );
                }
            }
        }
    }

    private void rxShiftAttention( BaseController attacker ) {
        getBaseController().tx( "You notice " + attacker.getCharacter().getShortDesc() + "'s attention shift to you!" );
    }

    private void rxShiftAttentionForAnother( BaseController attacker, BaseController attacked ) {
        getBaseController().tx( attacker.getCharacter().getShortDesc() + "'s attention shifts to " + attacked.getCharacter().getShortDesc() + "." );
    }

    public void txStartCombat( BaseController target ) {
        getBaseController().tx( "You start fighting " + target.getCharacter().getShortDesc() + "." );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                if ( target == other ) {
                    target.getEchoes().rxStartCombat( getBaseController() );
                }
                else {
                    other.getEchoes().rxStartCombatForAnother( getBaseController(), target );
                }
            }
        }
    }

    private void rxStartCombat( BaseController attacker ) {
        getBaseController().tx( attacker.getCharacter().getShortDesc() + " starts fighting you!" );
    }

    private void rxStartCombatForAnother( BaseController attacker, BaseController attacked ) {
        getBaseController().tx( attacker.getCharacter().getShortDesc() + " starts attacking " + attacked.getCharacter().getShortDesc() + "." );
    }

    public void txStartFightingBack( BaseController target ) {
        getBaseController().tx( "You start fighting " + target.getCharacter().getShortDesc() + " back." );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                if ( target == other ) {
                    target.getEchoes().rxStartFightingBack( getBaseController() );
                }
                else {
                    other.getEchoes().rxStartFightingBackForAnother( getBaseController(), target );
                }
            }
        }
    }

    private void rxStartFightingBack( BaseController attacker ) {
        getBaseController().tx( attacker.getCharacter().getShortDesc() + " starts fighting you back!" );
    }

    private void rxStartFightingBackForAnother( BaseController attacker, BaseController attacked ) {
        getBaseController().tx( attacker.getCharacter().getShortDesc() + " starts fighting " + attacked.getCharacter().getShortDesc() + " back." );
    }

    public void txStopFighting() {
        BaseController target = getBaseController().getCombatController().getTarget();

        getBaseController().tx( "You stop fighting " + target.getCharacter().getShortDesc() + "." );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                if ( target == other ) {
                    target.getEchoes().rxStopFighting( getBaseController() );
                }
                else {
                    other.getEchoes().rxStopFightingForAnother( getBaseController(), target );
                }
            }
        }
    }

    private void rxStopFighting( BaseController attacker ) {
        getBaseController().tx( attacker.getCharacter().getShortDesc() + " stops fighting you." );
    }

    private void rxStopFightingForAnother( BaseController attacker, BaseController attacked ) {
        getBaseController().tx( attacker.getCharacter().getShortDesc() + " stops fighting " + attacked.getCharacter().getShortDesc() + "." );
    }

    public void txFlee() {
        getBaseController().tx( "You desperately look for an escape route!" );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxFlee( getBaseController() );
            }
        }
    }

    private void rxFlee( BaseController who ) {
        getBaseController().tx( who.getCharacter().getShortDesc() + " desperately tries to find an escape route!" );
    }

    public void txStartFleeing() {
        getBaseController().tx( "You start looking for an escape route!" );

        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxStartFleeing( getBaseController() );
            }
        }
    }

    private void rxStartFleeing( BaseController who ) {
        getBaseController().tx( who.getCharacter().getShortDesc() + " starts looking for an escape route!" );
    }

    public void txDie() {
        getBaseController().tx( "Darkness engulfs you as you die..." );
        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.getEchoes().rxDie( getBaseController() );
            }
        }
    }

    private void rxDie( BaseController who ) {
        getBaseController().tx( who.getCharacter().getShortDesc() + " expires with a ragged exhalation." );
    }

    public void txGiveHandsFull( BaseController whom, ItemController what ) {
        getBaseController().tx( whom.getCharacter().getShortDesc() + " will need a free hand to take " + what.getShortDesc() + "." );

        whom.tx( getBaseController().getCharacter().getShortDesc() + " tries to give you " + what.getShortDesc() + ", but your hands are full." );
    }

    public void txGiveNotAccepted( BaseController whom, ItemController what ) {
        getBaseController().tx( whom.getCharacter().getShortDesc() + " does not accept " + what.getShortDesc() + "." );

        whom.tx( getBaseController().getCharacter().getShortDesc() + " tries to give you " + what.getShortDesc() + ", but you don't currently accept items." );
    }

    public void txEnterArea() {
        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.tx( getBaseController().getCharacter().getShortDesc() + " enters the area." );
            }
        }
    }

    public void txLeaveArea() {
        for ( BaseController other : getBaseController().getRoomController().getCharactersController().getBaseControllers() ) {
            if ( other != getBaseController() ) {
                other.tx( getBaseController().getCharacter().getShortDesc() + " leaves the area." );
            }
        }
    }

    public BaseController getBaseController() {
        return baseController;
    }

    public void setBaseController( BaseController baseController ) {
        this.baseController = baseController;
    }

}
